package com.service.scheduling.task;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.*;
import com.service.dao.mapper.JournalAccountMapper;
import com.service.dao.mapper.MemberMapper;
import com.service.dao.model.*;
import com.service.dao.po.MemberCache;
import com.service.scheduling.constants.IPFSConstant;
import com.service.scheduling.dto.MemberStoreChange;
import com.service.scheduling.service.*;
import com.service.scheduling.support.MemberCacheUtils;
import com.service.scheduling.support.MemberStoreLockUtils;
import com.service.scheduling.support.OrderProductHrComboLockUtils;
import com.service.scheduling.utils.DateUtil;
import com.service.scheduling.utils.MathUtil;
import com.service.scheduling.utils.MemCacheNoTran;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * <dl>
 * <dt>back-end</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2015</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019年12月15日</dd>
 * </dl>
 *
 * @author
 */
@Slf4j
@Component
@SuppressWarnings("all")
public class HrComboFILMiningRateTask {
	@Autowired
	private MemberCacheUtils memberCacheUtils;
	@Autowired
	private MemberMapper memberMapper;
	@Autowired
	private MemberStoreLockUtils memberStoreLockUtils;
	@Autowired
	private SysConfigService sysConfigService;
	@Autowired
	private MemberStoreService memberStoreService;
	@Autowired
	private MemberMiningOrderService memberMiningOrderService;
	@Autowired
	private ProductHrComboService productHrComboService;
	@Autowired
	private JournalAccountMapper journalAccountMapper;
	@Autowired
	private ConfigService configService;
	@Autowired
	private OrderProductHrComboService orderProductHrComboService;
	@Autowired
	private SysQuotesIpfxService sysQuotesIpfxService;
	@Autowired
	private MemCacheNoTran memCache;
	@Autowired
	private OrderProductHrComboLockUtils orderProductHrComboLockUtils;
	@Autowired
	private OrderProductHrComboPaddingService paddingService;
	@Autowired
	private MemberGradeConfigService gradeConfigService;
	@Autowired
	private CoinService coinService;
	@Autowired
	private MemberCacheService memberCacheService;
	@Autowired
	private MemberService memberService;

	private static BigDecimal baseNum = new BigDecimal(100L);
	private static BigDecimal directReleaseFILRate = new BigDecimal(25L);
	private static BigDecimal linearReleaseFILRate = new BigDecimal(75L);
	private static int linearReleaseEndDays = 180;

	/**
	 * 定时算力
	 */
	@Scheduled(cron = "0 10 0 * * ?")
//	@Scheduled(cron = "0 */8 * * * ?")
	public void doCalculate() {
		log.info("FIL算力定时任务-开始");
		String minerStatus = configService.getByConfigName(ConfigConstants.MINER_STATUS).getConfValue();
		if (MinerStatusEnum.OFF.equals(minerStatus)) {
			log.info("矿机暂停运行......");
			return;
		}
		Date currentDate = new Date();
		String pattern = "yyyy-MM-dd";
		String currentDateStr = DateFormatUtils.format(currentDate, pattern);
		Date yesterday = DateUtils.addDays(currentDate, -1);//昨天时间为当前时间的前一天
		String yesterdayStr = DateFormatUtils.format(yesterday, pattern);
		log.info("算力查询时间{}", currentDateStr);
		List<OrderProductHrCombo> hrComboList = orderProductHrComboService.listMiningOrder(CoinsEnum.FIL.getCode(), yesterday);
		if (ObjectUtils.isEmpty(hrComboList)) {
			log.info("{}暂无产币算力套餐订单", currentDateStr);
			return;
		}
		SysConfig comShareConf = configService.getByConfigName(ConfigConstants.COM_FEE_SHARE_RATE);
		BigDecimal comFeeShareRate = MathUtil.div(MathUtil.parseBigDecimal(comShareConf.getConfValue()),baseNum,4);
		//申明订单结束的数组
		List<Integer> endList = Lists.newArrayList();
		Coins FIL = coinService.getByCoinsName(CoinsEnum.FIL.getName());
		BigDecimal outputNum = FIL.getOutputNum();//日产出效率
		Map<String, SysConfig> miningConfigMap = configService.mapByConfigGroup(ConfigConstants.Group.award.getCode());
		BigDecimal directMiningFILRate = MathUtil.parseBigDecimal(miningConfigMap.get(ConfigConstants.FIL_DIRECT_MINING_RATE).getConfValue());
		BigDecimal linearMiningFILRate = MathUtil.parseBigDecimal(miningConfigMap.get(ConfigConstants.FIL_LINEAR_MINING_RATE).getConfValue());
		directMiningFILRate = MathUtil.div(directReleaseFILRate, baseNum, 4);//直接释放百分比
		linearMiningFILRate = MathUtil.div(linearReleaseFILRate, baseNum, 4);//线性释放百分比
		Integer linearEndDays = Integer.parseInt(miningConfigMap.get(ConfigConstants.LINEAR_RELEASE_END_DAYS).getConfValue());
		if (linearEndDays == 0) {
			linearEndDays = linearReleaseEndDays;
		}
		Integer finalLinearEndDays = linearEndDays;
		BigDecimal finalLinearMiningFILRate = linearMiningFILRate;
		BigDecimal finalDirectMiningFILRate = directMiningFILRate;

		hrComboList.parallelStream().forEach(item -> {
			//已支付且未挖矿的订单第二天凌晨开始挖矿
			if (ObjectUtils.nullSafeEquals(item.getOrderStatus(), HashRateOrderEnums.Status.payed.getCode())
					&& item.getStartTime().before(yesterday)) {
				log.info("算力订单<orderId:{}>还未开始", item.getId());
				return;
			}
			if (currentDate.after(item.getEndTime())) {
				log.info("算力订单<orderId:{}>已结束", item.getId());
				endList.add(item.getId());
				return;
			}
			if (DateUtils.isSameDay(currentDate, item.getEndTime())) {
				log.info("算力订单<orderId:{}>结算最后一天<{}>", item.getId(), currentDate);
				endList.add(item.getId());
			}
			Integer isMiningRecords = journalAccountMapper.checkByMemberAndTypeAndOrderAndSomeday(item.getMemberId(), item.getId(), currentDateStr, MemberJAType.getMiningIncomeType());
			if (isMiningRecords > 0) {
				log.info("算力订单<orderId:{}>,今日<{}>已发昨日<{}>挖矿所得", item.getId(), currentDateStr, yesterdayStr);
				return;
			}
			BigDecimal miningNum = getHrComboRateMining(item, outputNum, item.getCustodyFee());//该订单矿机释放给用户的数量
			BigDecimal comFeeNum = MathUtil.mul(item.getTotalHashRate(), outputNum,8).multiply(item.getCustodyFee());//该订单公司收取手续费
			if (MathUtil.isLEZero(miningNum)) {
				log.info("算力套餐订单<orderId:{}>昨日<{}>挖矿所得为0", item.getId(), yesterdayStr);
				return;
			}
			log.info("算力套餐订单<orderId:{}>昨日<{}>挖矿所得{}", item.getId(), yesterdayStr, miningNum);
			OrderProductHrComboDetails detail = orderProductHrComboService.getHrComboDetail(item.getId());
			boolean isLock = memberStoreLockUtils.lock(item.getMemberId(), CoinsEnum.FIL.getCode());
			if (!isLock) {
				return;
			}
			//25%直接释放到用户FIL余额
			BigDecimal immediately = MathUtil.mul(miningNum, finalDirectMiningFILRate, 8);

			memberStoreService.changeMemberStore(
					MemberStoreChange.builder()
							.memberId(item.getMemberId())
							.coinsId(CoinsEnum.FIL.getCode())
							.coinsName(CoinsEnum.FIL.getName())
							.jaType(MemberJAType.MINING)
							.orderId(item.getId())
							.orderDetailId(detail.getId())
							.changeNum(immediately)
							.remark(yesterdayStr + "FIL" +MemberJAType.MINING.getMsg())
							.build());
			memberStoreLockUtils.unlock(item.getMemberId(), CoinsEnum.FIL.getCode());

			//75%生成线性释放订单
			BigDecimal frostNum = MathUtil.mul(miningNum, finalLinearMiningFILRate, 8);
			//生产冻结线性释放订单
			MemberMiningOrder miningFrostOrder = new MemberMiningOrder();
			miningFrostOrder.setMemberId(item.getMemberId());
			miningFrostOrder.setCoinsId(CoinsEnum.FIL.getCode());
			miningFrostOrder.setCoinsName(CoinsEnum.FIL.getName());
			miningFrostOrder.setFrostNum(frostNum);
			miningFrostOrder.setOrderId(item.getId());
			miningFrostOrder.setLockDay(finalLinearEndDays);//默认冻结180天
			miningFrostOrder.setReleaseDay(0);//初始已释放天数0
			miningFrostOrder.setStartTime(DateUtil.nDaysAfterDate(1));//默认当天延后一天的凌晨0点开始
			miningFrostOrder.setIsFinish(MemberFrostMiningEnum.UNFINISHED.getCode());//默认状态为0 释放中
			miningFrostOrder.setTotalRelease(BigDecimal.ZERO);//初始释放金额0
			miningFrostOrder.setCreateTime(new Date());
			memberMiningOrderService.save(miningFrostOrder);
			//新增公司收益记录
			JournalAccount journalAccount = JournalAccount.builder()
					.storeId(0)
					.storeNum(BigDecimal.ZERO)
					.coinsId(CoinsEnum.FIL.getCode())
					.coinsName(CoinsEnum.FIL.getName())
					.storeFrostNum(BigDecimal.ZERO)
					.memberId(0)
					.jaNum(comFeeNum)
					.custodyFee(BigDecimal.ZERO)
					.jaType(MemberJAType.COM_FEE_REWARD.getCode())
					.jaRemark(MemberJAType.COM_FEE_REWARD.getMsg())
					.orderId(item.getId())
					.orderDetailId(detail.getId())
					.createTime(new Date())
					.build();
			journalAccountMapper.insert(journalAccount);
			//更新订单状态和累计收益
			item.setAccumulatedIncome(item.getAccumulatedIncome().add(immediately));
			item.setOrderStatus(HashRateOrderEnums.Status.payed_effective.getCode());
			orderProductHrComboService.updateById(item);
            //查询填充算力填充的订单信息
            if(!ObjectUtils.isEmpty(item) && item.getOrderStatus() == HashRateOrderEnums.Status.payed.getCode()){
				item.setOrderStatus(HashRateOrderEnums.Status.payed_effective.getCode());
                orderProductHrComboService.updateById(item);
            }
			try{
				//用户所在的上级用户算力提成
				MemberCache memberCache = memberCacheService.getMemberCache(item.getMemberId());
				String userPath = null;
				if(!ObjectUtils.isEmpty(memberCache)){
					userPath = memberCache.getUserPath();
				}
				if(StringUtils.isEmpty(userPath)){
					Member member = memberService.getById(item.getMemberId());
					if(!ObjectUtils.isEmpty(member) && !ObjectUtils.isEmpty(member.getUserPath())) {
						userPath = member.getUserPath();
					}else{
						return;
					}
				}
				List<Integer> teamIds = splitMemberId(userPath);
				if(!ObjectUtils.isEmpty(teamIds)){
					comFeeNum = comFeeNum.multiply(comFeeShareRate);
					rewardTopTeam(teamIds, memberCache.getMemberGrade(), comFeeNum, detail);
				}
			}catch (Exception e){
				log.info("算力订单<{}>向上级团队发放算力提成异常:by{}",item.getId(),e.getCause());
			}
		});
		//修改过期算力订单
		orderProductHrComboService.updateHrComboRateOrder2Complete(endList);
		log.info("FIL算力定时任务-结束");
	}


	/**
	 * @Description: 用户上级无限返利
	 * @Author: ty
	 * @Date: 2021/6/16 18:45
	 * @param: [teamIds, miningNum]
	 * @return: void
	 **/
	private void rewardTopTeam(List<Integer> teamIds, Integer currentGrade, BigDecimal miningNum, OrderProductHrComboDetails detail) {
		Map<Integer, MemberGradeConfig> gradeConfigMap = gradeConfigService.list2Map();
		Date currentDate = new Date();
		String pattern = "yyyy-MM-dd";
		Date yesterday = DateUtil.getDayOfStart(DateUtils.addDays(currentDate, -1));
		String yesterdayStr = DateFormatUtils.format(yesterday, pattern);
		if (!ObjectUtils.isEmpty(teamIds) && miningNum.compareTo(BigDecimal.ZERO) > 0) {
			BigDecimal releasedAwardRate = new BigDecimal(0);
			for (Integer memberId : teamIds) {
				MemberCache memberCache = memberCacheService.getMemberCache(memberId);
				if (ObjectUtils.isEmpty(memberCache)) {
					log.info("用户信息未找到");
					continue;
				}
				if(currentGrade >= GradeEnum.eleven.getValue() && memberCache.getMemberGrade() >= GradeEnum.eleven.getValue()){
					if(memberCache.getMemberGrade() <= currentGrade){
						log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
						continue;
					}
				}else if(currentGrade < GradeEnum.eleven.getValue() && memberCache.getMemberGrade() < GradeEnum.eleven.getValue()){
					if(memberCache.getMemberGrade() <= currentGrade){
						log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
						continue;
					}
				}else if(currentGrade >= GradeEnum.eleven.getValue() && memberCache.getMemberGrade() < GradeEnum.eleven.getValue()){
					if((memberCache.getMemberGrade()+10) <= currentGrade){
						log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
						continue;
					}
				}else if(currentGrade < GradeEnum.eleven.getValue() && memberCache.getMemberGrade() >= GradeEnum.eleven.getValue()){
					if(memberCache.getMemberGrade() <= (currentGrade+10)){
						log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
						continue;
					}
				}
				MemberGradeConfig gradeConfig = gradeConfigMap.get(memberCache.getMemberGrade());
				BigDecimal teamMemberRate = MathUtil.div(gradeConfig.getTeamMemberRate(), baseNum, 4);
				BigDecimal acc = teamMemberRate.subtract(releasedAwardRate);//极差减掉已发
				if(acc.compareTo(new BigDecimal(0)) <= 0){
					continue;
				}
				log.info("会员<{}>等级<{}>发放等级极差奖励=> 释放团队速率<{}>，上一级已发速率<{}>",memberCache.getMemberId(),
						memberCache.getMemberGrade(),acc,releasedAwardRate);
				releasedAwardRate = gradeConfig.getTeamMemberRate();//设置当前会员级别已发值
				BigDecimal rewardNum = MathUtil.mul(miningNum, acc, 8);
				boolean isLock = memberStoreLockUtils.lock(memberId, detail.getCoinsId());
				if (!isLock) {
					continue;
				}
				if (miningNum.compareTo(BigDecimal.ZERO) <= 0) {
					continue;
				}
				memberStoreService.changeMemberStore(
						MemberStoreChange.builder()
								.memberId(memberId)
								.coinsId(detail.getCoinsId())
								.coinsName(detail.getCoinsName())
								.jaType(MemberJAType.RELEASE_PRODUCT_BY_TEAM)
								.orderId(detail.getHrOrderId())
								.orderDetailId(detail.getId())
								.changeNum(rewardNum)
								.remark(yesterdayStr + MemberJAType.RELEASE_PRODUCT_BY_TEAM.getMsg())
								.build());
				memberStoreLockUtils.unlock(memberId, detail.getCoinsId());
			}
		}
	}

	/**
	 * @Description: 根据用户路径获取用户团队
	 * @Author: ty
	 * @Date: 2021/6/16 18:35
	 * @param: [userPath]
	 * @return: java.util.List<java.lang.Integer>
	 **/
	public List<Integer> splitMemberId(String userPath) {
		if(ObjectUtils.isEmpty(userPath)){
			return null;
		}
		String[] teamIds = userPath.split("-");
		List<Integer> memberIds = new ArrayList<>();
		for (String idStr : teamIds) {
			if (!ObjectUtils.isEmpty(idStr)) {
				memberIds.add(Integer.parseInt(idStr));
			}
		}
		//倒叙排列
		Collections.sort(memberIds, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				// 返回值为int类型，大于0表示正序，小于0表示逆序
				return o2 - o1;
			}
		});
		return memberIds;
	}

	/**
	 * 更新今日订单状态
	 */
	@Scheduled(cron = "0 2 0 * * ?")
//	@Scheduled(cron = "0 */5 * * * ?")
	public void updateTodayHrRate() {

		log.info("FIL更新今日开挖算力状态-开始");
		Date currentDate = new Date();
		Date today = DateUtil.getDayOfStart(currentDate);

		List<OrderProductHrComboPadding> hrComboList = paddingService.listBeforeCurrentDate(CoinsEnum.FIL.getCode(), today);
		if (ObjectUtils.isEmpty(hrComboList)) {
			log.info("暂无算力套餐订单");
			return;
		}

		hrComboList.parallelStream().forEach(item -> {
			if (item.getPaddingStatus() == HashRateOrderEnums.Status.payed.getCode()) {
				//更新今日已付款，开挖的订单状态
				OrderProductHrComboPadding miningCombo = new OrderProductHrComboPadding();
				miningCombo.setId(item.getId());
				miningCombo.setPaddingStatus(HashRateOrderEnums.Status.payed_effective.getCode());
				paddingService.updateById(miningCombo);
			}
		});
		log.info("FIL更新今日开挖算力状态-结束");
	}

	/**
	 * 得到挖矿产出量
	 *
	 * @param order
	 * @param coins
	 * @param miningRate
	 * @return
	 */
	public BigDecimal getHrComboRateMining(OrderProductHrCombo hrCombo, BigDecimal outputNum, BigDecimal miningRate) {
		BigDecimal totalNum = MathUtil.mul(hrCombo.getTotalHashRate(), outputNum, 8);
		return MathUtil.mul(totalNum, BigDecimal.ONE.subtract(miningRate), 8);
	}


}
